﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using JigLibX.Physics;
using JigLibX.Collision;
using JigLibX.Math;
using JiggleGame.PhysicObjects;
using JigLibX.Geometry;
using System.Diagnostics;
using XNADota.UI;
using XNADota.GamePlay.Ability;

namespace XNADota.GamePlay
{
    public class UnitObject : BasicObject
    {
        // physics property
        protected Body _Body;
        protected CollisionSkin _CollisionSkin;
        protected PhysicObject _VisibleObject;

        // Internal Properties
        protected Vector3 _SideLengths;
        protected bool _IsBuilding;

        // Unit ability
        protected bool _IsInvulnerable;
        protected bool _CanSelectHero;

        public Dictionary<string, AbilityObject> Abilities;

        public UnitObject(string id)
            : base(id, EObjectType.UnitData)
        {
            CreatePhysicsBody(id);


            // read abilities
            string abilityString = GameplayWorld.Instance.ObjectsManager.GetFiledValue(EObjectType.UnitData, id, "abilList");
            string[] abilitiesString = abilityString.Split(new char[] { ',' });

            Abilities = new Dictionary<string, AbilityObject>();
            for (int i = 0; i < abilitiesString.Length; i++)
            {
                string key = abilitiesString[i];
                Abilities.Add(key, new AbilityObject(key));

                if (Abilities[key].MerchantAbility != null)
                {
                    Abilities[key].MerchantAbility.CreateMerchant(id);
                }
            }
        }

        #region Gameplay
        public void Update(GameTime gameTime)
        {
        }
        #endregion

        #region Physics

        public bool ShowPhysics
        {
            set
            {
                _VisibleObject.IsVisible = value;
            }
            get
            {
                return _VisibleObject.IsVisible;
            }
        }

        public void CreatePhysicsBody(string Id)
        {
            this.IsSelectable = true;

            string collisionSizeString = GameplayWorld.Instance.ObjectsManager.GetFiledValue(EObjectType.UnitData, Id, "collision");
            float collisionSize = float.Parse(collisionSizeString) * 128.0f / 100.0f;
            _SideLengths = new Vector3(collisionSize, collisionSize, collisionSize);

            _Body = new Body();
            _CollisionSkin = new CollisionSkin(_Body);
            _CollisionSkin.AddPrimitive(new Box(-0.5f * _SideLengths, this.Orientation, _SideLengths), new MaterialProperties(0.8f, 0.8f, 0.7f));
            _Body.CollisionSkin = this._CollisionSkin;
            Vector3 com = SetMass(100.0f);
            _Body.MoveTo(this.Position, this.Orientation);
            _CollisionSkin.ApplyLocalTransform(new Transform(-com, Matrix.Identity));
            _Body.EnableBody();

            _BoundingBox = new BoundingBox(_Body.Position - 0.5f * _SideLengths, _Body.Position + 0.5f * _SideLengths);

            // show physics
            _VisibleObject = new BoxObject(XNADota.Instance, _SideLengths, _Body);
            XNADota.Instance.Components.Add(_VisibleObject);
            ShowPhysics = false;

            // Building is immovable
            string isBuildingString = GameplayWorld.Instance.ObjectsManager.GetFiledValue(EObjectType.UnitData, Id, "isbldg");
            _IsBuilding = Convert.ToBoolean(int.Parse(isBuildingString));
            if (_IsBuilding)
            {
                _Body.Immovable = true;
                this.IsMovable = false;
            }
            else
            {
                this.IsMovable = true;
            }

            // Select scale
            string scaleString = GameplayWorld.Instance.ObjectsManager.GetFiledValue(EObjectType.UnitData, Id, "scale");
            SelectableCircleScale = Vector3.One * float.Parse(scaleString);
        }

        public void ApplyPosition()
        {
            _Body.MoveTo(this.Position + new Vector3(0, _SideLengths.Y / 2, 0), _Body.Orientation);
            _BoundingBox = new BoundingBox(_Body.Position - 0.5f * _SideLengths, _Body.Position + 0.5f * _SideLengths);
        }

        public void UpdateAfterPhysicsIntergration()
        {
            this.Position = _Body.Position;
            this.Orientation = _Body.Orientation;

            _BoundingBox.Min = _Body.Position - 0.5f * _SideLengths;
            _BoundingBox.Max = _Body.Position + 0.5f * _SideLengths;
        }

        protected Vector3 SetMass(float mass)
        {
            PrimitiveProperties primitiveProperties =
                new PrimitiveProperties(PrimitiveProperties.MassDistributionEnum.Solid, PrimitiveProperties.MassTypeEnum.Density, mass);

            float junk; Vector3 com; Matrix it, itCoM;

            _CollisionSkin.GetMassProperties(primitiveProperties, out junk, out com, out it, out itCoM);
            _Body.BodyInertia = itCoM;
            _Body.Mass = junk;

            return com;
        }
        #endregion

        #region Graphics
        private BoundingBox _BoundingBox;

        public override void SetSelected(bool selected)
        {
            IsSelected = selected;
        }

        public override bool Select(Microsoft.Xna.Framework.Ray ray)
        {
            Segment seg = new Segment(ray.Position, ray.Direction * 100000.0f);
            float frac; Vector3 pos, normal;
            return _CollisionSkin.SegmentIntersect(out frac, out pos, out normal, seg);
        }

        public override void Update(BoundingFrustum BoundingFrustum)
        {
            if (BoundingFrustum.Contains(this._BoundingBox) != ContainmentType.Disjoint)
                IsVisible = true;
            else
                IsVisible = false;
        }

        public override ContainmentType BeContained(BoundingBox BoundingBox)
        {
            return BoundingBox.Contains(_BoundingBox);
        }
        #endregion
    }
}
